using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.U2D;
// using Spine;
using System.Text.RegularExpressions;
// using Sirenix.OdinInspector;

public class AssetsMgr : MonoBehaviour
{
    private static AssetsMgr _inst;
    public static AssetsMgr Inst
    {
        get
        {
            if (_inst == null)
            {
                AssetsMgr managerInScene = UnityEngine.Object.FindObjectOfType<AssetsMgr>();
                if (managerInScene != null)
                {
                    _inst = managerInScene;
                }
                else
                {
                    GameObject managerObject = new GameObject { name = "AssetsMgr" };
                    _inst = managerObject.AddComponent<AssetsMgr>();
                    GameObject.DontDestroyOnLoad(managerObject);
                }
                _inst.Init();
            }

            return _inst;
        }
    }

#if UNITY_STANDALONE_OSX
    public static string PLATFORM_NAME = "StandaloneOSXUniversal";
#elif UNITY_STANDALONE_WIN
    public static string PLATFORM_NAME = "StandaloneWindows";
#elif UNITY_ANDROID
    public static string PLATFORM_NAME = "Android";
#elif UNITY_IOS
    public static string PLATFORM_NAME = "iOS";
#elif UNITY_WEBGL
    public static string PLATFORM_NAME = "WeixinMiniGame";
#elif WEIXINMINIGAME
    public static string PLATFORM_NAME = "WeixinMiniGame";
#endif

    private HashSet<string> _downloading = new HashSet<string>();
    private Dictionary<string, AssetBundle> _bundles = new Dictionary<string, AssetBundle>();
    private Dictionary<string, string> _namesDict = new Dictionary<string, string>();
    private AssetBundleManifest _manifest;

    private static string _resPrefix = "Assets/Content";
    private string altasPrefix = "atlas/";
    private HashSet<string> _used = new HashSet<string>();
    public T LoadAsset<T>(string name) where T : UnityEngine.Object
    {
#if UNITY_EDITOR
        if (!Application.isPlaying || !GameRoot.Inst.useAB)
        {
            var bundleName = Path.GetDirectoryName(name).ToLower();
            if (!_used.Contains(bundleName))
            {
                _used.Add(bundleName);
            }

            var path = Path.Combine(_resPrefix, name);
            T asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
            if (asset == null)
            {
                return default(T);
            }
            return asset;
        }
        else
        {
            return LoadAssetFromBundle<T>(name);
        }
#else
        return LoadAssetFromBundle<T>(name);
#endif
    }

    public GameObject LoadPrefab(string prefab)
    {
        var asset = LoadAsset<GameObject>(prefab + ".prefab");
        if (asset)
        {
            var go = (GameObject)GameObject.Instantiate(asset);
            go.name = asset.name;
            return go;
        }
        return null;
    }

    public GameObject LoadPrefab(string prefab, Transform parent)
    {
        var asset = LoadAsset<GameObject>(prefab + ".prefab");
        if (asset)
        {
            var go = (GameObject)GameObject.Instantiate(asset, parent);
            go.name = asset.name;
            return go;
        }
        return null;
    }

    public void Init()
    {
        SpriteAtlasManager.atlasRequested += OnAtlasRequested;
    }

    public void Clear()
    {
        SpriteAtlasManager.atlasRequested -= OnAtlasRequested;
    }

    private void OnAtlasRequested(string tag, Action<SpriteAtlas> action)
    {
        var sa = LoadAtlasAsset(tag);
        if (sa == null) { Debug.LogError("未读取到图集:" + tag); }
        action?.Invoke(sa);
    }

    public SpriteAtlas LoadAtlasAsset(string name)
    {
#if UNITY_EDITOR
        if (!GameRoot.Inst.useAB)
            return (SpriteAtlas)LoadAsset<SpriteAtlas>($"Atlas/{name}.spriteatlas");
        else
        {
            var bundle = LoadAssetBundle(altasPrefix + name.ToLower());
            var sa = bundle.LoadAsset<SpriteAtlas>(name);
            return sa;
        }
#else
        var bundle = LoadAssetBundle(altasPrefix + name.ToLower());
        var sa = bundle.LoadAsset<SpriteAtlas>(name);
        return sa;
#endif
    }

    public AudioClip LoadAudio(string name)
    {
        return LoadAsset<AudioClip>(name);
    }

    public T LoadAssetFromBundle<T>(string name) where T : UnityEngine.Object
    {
        var bundleName = Path.GetDirectoryName(name).ToLower();
        var bundle = LoadAssetBundle(bundleName);
        if (bundle != null)
        {
            var path = Path.Combine(_resPrefix, name);
            return bundle.LoadAsset<T>(path);
        }

        return default(T);
    }

    public bool IsMainifestLoaded()
    {
        return _manifest != null;
    }

    public IEnumerator DownloadAssetBundle(string filename, Action onEnd = null)
    {
        filename = filename.ToLower();
        if (_bundles.ContainsKey(filename))
        {
            yield break;
        }

        yield return StartCoroutine(Download(filename));
        if (_manifest == null)
        {
            var manifestBundle = LoadBundleFile(PLATFORM_NAME);
            _manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        var dependencies = _manifest.GetAllDependencies(_namesDict[filename]);
        if (dependencies != null)
        {
            for (var i = 0; i < dependencies.Length; i++)
            {
                var dependentName = _namesDict[dependencies[i]];
                if (!_bundles.ContainsKey(dependentName))
                {
                    yield return StartCoroutine(Download(dependentName));
                }
            }
        }
        onEnd?.Invoke();
    }
    
    // 生成字典 abName -> abNameWithHash
    private Dictionary<string,string> GetABNamesWithHash(AssetBundleManifest abm) 
    {
        var hashNames = abm.GetAllAssetBundles();
        Dictionary<string, string> ABNamesDict = 
            new Dictionary<string, string>();
        foreach (var hashName in hashNames)
        {
            //需要注意AB后缀名，为ab名称后+_32位hash值
            var abName = Regex.Match(hashName,
            "_[0-9a-f]{32}").Success 
            ? hashName.Substring(0,hashName.Length - 33)
            : hashName;
            Debug.Log($"abName: {abName}, hashName: {hashName}");
            ABNamesDict.Add(abName, hashName);
            // 反过来也要记录一下，可以用上
            ABNamesDict.Add(hashName, abName);
        }
        return ABNamesDict;
    }

    private IEnumerator Download(string filename, bool root = false)
    {
        if (_downloading.Contains(filename))
            yield break;

        _downloading.Add(filename);
        // 下载
        var url = GetBundlePath(filename, root);
        UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(url);
        yield return www.SendWebRequest();
        AssetBundle ab = (www.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
        CacheBundle(filename, ab);
        _downloading.Remove(filename);
        Debug.Log($"{filename} download succ, from: {url}");
    }

    private void CacheBundle(string filename, AssetBundle ab)
    {
        _bundles.Add(filename, ab);
        // SkeletonDataMgr.Inst.AddAB(filename, ab);
    }

    public IEnumerator DownloadManifest()
    {
        yield return StartCoroutine(Download(PLATFORM_NAME, true));
        var manifestBundle = _bundles[PLATFORM_NAME];
        _manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        _namesDict = GetABNamesWithHash(_manifest);
    }

    // [Button]
    public void PrintUsed()
    {
        foreach (var item in _used)
        {
            Debug.Log(item);
        }
    }

    private AssetBundle LoadAssetBundle(string bundleName)
    {
        if (_bundles.ContainsKey(bundleName))
            return _bundles[bundleName];

        var bundle = LoadBundleFile(bundleName);
        if (bundle != null)
        {
            if (_manifest == null)
            {
                var manifestBundle = LoadBundleFile(PLATFORM_NAME);
                _manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }

            var dependencies = _manifest.GetAllDependencies(bundleName);
            if (dependencies != null)
            {
                for (var i = 0; i < dependencies.Length; ++i)
                {
                    LoadBundleFile(dependencies[i]);
                }
            }
        }

        return bundle;
    }

    private string GetBundlePath(string bundleName, bool root = false)
    {
        string path = "";
        if (root)
        {
            // path = Path.Combine(AutoStreaming.CustomCloudAssetsRoot, bundleName);
        }
        else
        {
            bundleName = _namesDict[bundleName];
            // path = Path.Combine(AutoStreaming.CustomCloudAssetsRoot, "CustomAB", PLATFORM_NAME, bundleName);
        }
        return path;
    }

    private AssetBundle LoadBundleFile(string bundleName)
    {
        if (_bundles.ContainsKey(bundleName))
            return _bundles[bundleName];

        var path = GetBundlePath(bundleName);
        AssetBundle bundle = AssetBundle.LoadFromFile(path);
        if (bundle)
        {
            _bundles.Add(bundleName, bundle);
            return bundle;
        }

        return null;
    }
}
